package health_system.health_system.service.impl;

import health_system.health_system.config.AppProperties;
import health_system.health_system.entity.RefreshToken;
import health_system.health_system.entity.User;
import health_system.health_system.exception.BusinessException;
import health_system.health_system.repository.RefreshTokenRepository;
import health_system.health_system.repository.UserRepository;
import health_system.health_system.service.RefreshTokenService;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@Service
@RequiredArgsConstructor
public class RefreshTokenServiceImpl implements RefreshTokenService {

    private final RefreshTokenRepository refreshTokenRepository;
    private final UserRepository userRepository;
    private final AppProperties appProperties;

    @Override
    @Transactional
    public RefreshToken createRefreshToken(Long userId, String deviceId, String deviceType, String ipAddress) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("User not found with id: " + userId));
        
        // Check if there's already a token for this device
        if (deviceId != null && !deviceId.isEmpty()) {
            List<RefreshToken> existingTokens = refreshTokenRepository.findByUserAndDeviceId(user, deviceId);
            if (!existingTokens.isEmpty()) {
                // Delete existing tokens for this device
                existingTokens.forEach(refreshTokenRepository::delete);
            }
        }
        
        // Create new refresh token
        RefreshToken refreshToken = RefreshToken.builder()
                .user(user)
                .token(UUID.randomUUID().toString())
                .expiryDate(Instant.now().plusMillis(appProperties.getJwt().getRefreshExpiration()))
                .deviceId(deviceId)
                .deviceType(deviceType)
                .ipAddress(ipAddress)
                .revoked(false)
                .createdAt(Instant.now())
                .build();

        return refreshTokenRepository.save(refreshToken);
    }

    @Override
    public RefreshToken verifyExpiration(RefreshToken token) {
        if (token.isRevoked()) {
            refreshTokenRepository.delete(token);
            throw new BusinessException("Refresh token was revoked. Please make a new login request");
        }
        
        if (token.getExpiryDate().compareTo(Instant.now()) < 0) {
            refreshTokenRepository.delete(token);
            throw new BusinessException("Refresh token was expired. Please make a new login request");
        }

        return token;
    }

    @Override
    public Optional<RefreshToken> findByToken(String token) {
        return refreshTokenRepository.findByToken(token);
    }

    @Override
    @Transactional
    public void deleteToken(RefreshToken token) {
        refreshTokenRepository.delete(token);
    }

    @Override
    @Transactional
    public void deleteByUser(User user) {
        refreshTokenRepository.deleteByUser(user);
    }

    @Override
    public List<RefreshToken> findAllByUser(User user) {
        return refreshTokenRepository.findAllByUser(user);
    }

    @Override
    @Transactional
    public void revokeAllUserTokens(User user) {
        refreshTokenRepository.revokeAllUserTokens(user);
    }

    @Override
    @Transactional
    public void revokeUserTokensForDevice(User user, String deviceId) {
        refreshTokenRepository.revokeUserTokensForDevice(user, deviceId);
    }

    @Override
    @Transactional
    @Scheduled(cron = "0 0 */6 * * *") // Run every 6 hours
    public void cleanExpiredTokens() {
        log.info("Cleaning expired refresh tokens");
        refreshTokenRepository.deleteAllExpiredTokens(Instant.now());
    }
} 